package plateau;

import cartes.Carte;
import cartes.Classe;
import cartes.Objet;
import cartes.Race;
import common.Constantes;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

public class Inventaire implements Serializable {


    /*
     * Attributs
     */
    protected ArrayList<Carte> listeCartes;

    public ArrayList<Carte> getListeCartes() {
        return listeCartes;
    }
    //

    /*
     * Constructeurs
     */
    // Constructeur 1 : cas où l'inventaire est vide
    public Inventaire() {
        this.listeCartes = new ArrayList<Carte>();
    }

    // Constructeur 2 : cas où l'inventaire possède déjà au moins une carte
    public Inventaire(ArrayList<Carte> listeCartes) {
        this.listeCartes = listeCartes;
    }
    //

    /*
     * Méthodes
     */

    /*
     * Supprime une carte (passée en paramètre) de l'inventaire
     * @param : la carte à supprimer de l'inventaire
     * @author : Antonin
     * @lastChange : Arthur - rename "ajouter" en "add" pour une appelation intuitive
     */
    // On ajoute la référence de la carte passée en paramètre à l'inventaire
    public void add(Carte c) {
        this.listeCartes.add(c);
    }
    //

    /*
     * Ajoute une carte (passée en paramètre) à l'inventaire
     * @param : la carte à ajouter à l'inventaire
     * @lastChange : Arthur - rename "supprimer" en "remove" pour une appelation intuitive
     * @author : Antonin
     */
    // On supprime la référence de la carte passée en paramètre de l'inventaire
    public void remove(Carte c) {
        this.listeCartes.remove(c);
    }
    //


    /*
     * Calcule les points des cartes de l'inventaire du joueur
     * @return : le nombre de points total de l'inventaire
     * @author : Antonin
     * @lastChange : Arthur H - ajout des bonus specs
     */
    // On calcule la force selon les cartes Bonus (et uniquement Bonus)
    // présentent sur l'inventaire
    public int calculerForceInventaire() {

        int nbPoints = 0;

        // On récupère la valeur des bonus de toutes les cartes objets
        for (Carte c : listeCartes) {

            // Si la carte est une carte objet alors on récupère son nombre
            // de points
            // Arthur H : ATTENTION, un == sur des Objects (ex : String) va comparer les références, et pas les valeurs
            if (c.getClass().getName().equals(Constantes.C_OBJET)) {
                Objet o = (Objet) c;
                nbPoints += o.getBonus();
                if( this.getRace().contains(o.getRaceBonus()) || this.getClasse().contains(o.getClasseBonus()) ) {
                    nbPoints += o.getBonusSpecifique();
                }
            }
            //
        }
        //
        
        return nbPoints;
    }
    //

    /* 
     * Retourne la carte à l'indice demandé de l'inventaire
     * @param : id de la carte
     * @return : la carte demandée
     * @author : Arthur
     */
    public Carte getCarte(int id) {
        return this.listeCartes.get(id);
    }

    /*
     * Renvoie vrai si la liste de carte est vide, faux sinon
     * @return : booléen, vrai si vide, faux si pas vide
     * @author : Arthur
     */
    public Boolean isEmpty() {
        return this.listeCartes.isEmpty();
    }

    /*
     * Vide la pioche
     * Attention : penser à récupérer la liste, sinon les cartes sont perdues
     * @return : La liste des cartes supprimées de la pioche
     * @author : Arthur
     */
    public List<Carte> vider() {
        List<Carte> l = new ArrayList<Carte>(this.listeCartes);
        this.listeCartes.clear();
        return l;
    }

    /*
     * Getter / Setter
     */
    public ArrayList<Integer> getClasse() {
        ArrayList<Integer> out = new ArrayList<Integer>();
        for (Carte c : this.listeCartes) {
            if (c.getClass().getName().equals(Constantes.C_CLASSE)) {
                out.add(((Classe) c).getTypeClasse());
            }
        }
        return out;
    }


    /*
     * Getter / Setter
     */
    public ArrayList<Carte> getCartesClasse() {
        ArrayList<Carte> out = new ArrayList<Carte>();
        for (Carte c : this.listeCartes) {
            if (c.getClass().getName().equals(Constantes.C_CLASSE)) {
                out.add(c);
            }
        }
        return out;
    }


    public ArrayList<Integer> getRace() {
        ArrayList<Integer> out = new ArrayList<Integer>();
        for (Carte c : this.listeCartes) {
            if (c.getClass().getName().equals(Constantes.C_RACE)) {
                    out.add(((Race) c).getRace());
            }
        }
        if( out.isEmpty() || (out.size() == 1 && out.contains(Constantes.SANG_MELE ))) 
            out.add(Constantes.HUMAIN);
        return out;
    }
    
    public ArrayList<Carte> getCartesRace() {
        ArrayList<Carte> out = new ArrayList<Carte>();
        for (Carte c : this.listeCartes) {
            if (c.getClass().getName().equals(Constantes.C_RACE)) {
                out.add(c);
            }
        }
        return out;
    }

    /*
     * Teste si la partie du corps est déjà occupée ou disponible pour un nouvel objet
     * @param : type : Constant liée à la partie du corps (EQU_x)
     * @return : true si l'objet est équipable, false sinon
     */
    public boolean isAvailable(int type) {
        // Si c'est du UNDEF ça passe
        if( type == Constantes.UNDEFINED )
            return true;
        
        // Si l'inventaire contient déjà un objet de ce type, return false (SAUF pour UNE main)
        if (this.containsType(type) && type != Constantes.EQU_MAIN && type != Constantes.EQU_2_MAINS) {
            return false;
        }

        // Si on veut équiper un objet UNE main
        if (type == Constantes.EQU_MAIN) {
            // Si on trouve deux occurences différentes d'objet type "main" dans l'inventaire, ou un objet 2 mains, les mains sont prises
            if (this.containsType(Constantes.EQU_2_MAINS) || this.containsType(Constantes.EQU_MAIN, 2)) {
                return false;
            }
        }
        else if (type == Constantes.EQU_2_MAINS) {
            // Si on trouve deux occurences différentes d'objet type "main" dans l'inventaire, ou un objet 2 mains, les mains sont prises
            if (this.containsType(Constantes.EQU_2_MAINS) || this.containsType(Constantes.EQU_MAIN)) {
                return false;
            }
        }

        // Si tout s'est bien passé, c'est que c'est dispo
        return true;
    }

    /*
     * Recherche le type demandé dans l'inventaire
     * @param : partie du cors à rechercher
     * @return : vrai si la partie du corps est équipée dans l'inventaire
     */
    public boolean containsType(int type) {
        // Parcours de l'inventaire
        for (Carte c : this.listeCartes) {
            // Carte de type classe
            if (c.getClass().getName().equals(Constantes.C_OBJET)) {
                // L'objet est du type (partie du corps) correspondant
                if (((Objet) c).getType() == type) {
                    return true; // Trouvé !
                }
            }
        }
        return false;
    }

    /*
     * Surcharge ft précédente
     * @param : 
     *  + nombre d'occurence de la partie du corps
     */
    public Boolean containsType(int type, int nb) {
        int i = 0;
        for (Carte c : this.listeCartes) {
            if (c.getClass().getName().equals(Constantes.C_OBJET)) {
                if (((Objet) c).getType() == type) {
                    // Si on a trouvé suffisamment d'occurences
                    if (++i == nb) {
                        return true;
                    }
                }
            }
        }
        return false;
    }

    /*
     * Récupère les cartes correspondant au type demandé
     * @param : Le type d'objet de demandé
     * @return : La liste des cartes correspondantes dans l'iventaire
     * @author : Arthur
     */
    public ArrayList<Carte> getType( int type ) {
        ArrayList<Carte> l = new ArrayList<Carte>();
        
        for (Carte c : this.listeCartes) {
            if (c.getClass().getName().equals(Constantes.C_OBJET)) {
                if (type == Constantes.UNDEFINED || ((Objet) c).getType() == type) {
                    // Si on a trouvé suffisamment d'occurences
                    l.add(c);
                }
            }
        }
        
        return l;
    }
    
    /*
     * Teste si le sexe a changé grâce à une carte de l'équipement
     * @return : true, si la carte de changement de sexe est équipée, false sinon
     */
    public Boolean hasSexChanged() {
        for (Carte c : this.listeCartes) {
            if (c.getClass().getName().equals(Constantes.C_OBJET)) {
                if (((Objet) c).getType() == Constantes.EQU_SEXE) {
                    return true;
                }
            }
        }
        return false;
    }
    
    public int getNbGros() {
        int nb = 0;
        for (Carte c : this.listeCartes) {
            if (c.getClass().getName().equals(Constantes.C_OBJET)) {
                if (((Objet) c).getGros() == true) {
                    nb++;
                }
            }
        }
        return nb;
    }
    
    public ArrayList<Carte> getGros() {
        ArrayList<Carte> out = new ArrayList<Carte>();
        for (Carte c : this.listeCartes) {
            if (c.getClass().getName().equals(Constantes.C_OBJET)) {
                if (((Objet) c).getGros() == true) {
                    out.add(c);
                }
            }
        }
        return out;
    }
    
    public Carte getHigher() {
        int max = 0;
        Carte cMax = null;
        
        for (Carte c : this.listeCartes) {
            if (c.getClass().getName().equals(Constantes.C_OBJET)) {
                if (((Objet) c).getBonus() > max) {
                    max = ((Objet) c).getBonus();
                    cMax = c;
                }
            }
        }
        
        return cMax;
    }
    
    boolean compatibiliteRaceObjet(Carte race) {
        for ( Carte c : this.listeCartes ) {
            if( c.getClass().getName().equals(Constantes.C_OBJET)) {
                Objet o = (Objet) c;
                if( o.getRaceInterdite() == ((Race) race).getRace())
                    return false;
            }
        }
        return true;
    }
    
    public boolean compatibiliteClasseObjet(Carte classe) {
        for ( Carte c : this.listeCartes ) {
            if( c.getClass().getName().equals(Constantes.C_OBJET)) {
                Objet o = (Objet) c;
                if( o.getClasseInterdite() == ((Classe) classe).getTypeClasse())
                    return false;
            }
        }
        return true;
    }
    
    public boolean isDefausserClasseOk(Classe classe) {
        for ( Carte c : this.listeCartes ) {
            if( c.getClass().getName().equals(Constantes.C_OBJET)) {
                Objet o = (Objet) c;
                if( o.getClasse() == classe.getTypeClasse())
                    return false;
            }
        }
        return true;
    }
    
    public boolean isDefausserRaceOk(Race race) {
        for ( Carte c : this.listeCartes ) {
            if( c.getClass().getName().equals(Constantes.C_OBJET)) {
                Objet o = (Objet) c;
                if( o.getRace() == race.getRace())
                    return false;
            }
        }
        return true;
    }
}

 